<?php

require_once ('log4php/Logger.php');

class ShiftCalculator {

    private $logger;


    const SAME_DAY =0;
    const NEXT_DAY =1;
    const PREVIOUS_DAY=2;

    private $maxHoursPerShift = 8;
    private $minHoursPerShift = 1;
    private $timePattern = "(([0-1][0-9])|([2][0-4])):[0-5][0-9]";
    private $rePattern;
    //private $rePattern = "/^" . $this->timePattern . "\-[0-1][0-9]:[0-5][0-9]/";
    //private $rePattern = "/^[0-1][0-9]:[0-5][0-9]/";
    private $precision = 2;
    public function __construct() {
        $this->logger = Logger::getLogger('ShiftCalculator');
        $this->rePattern = "/^" . $this->timePattern . "\-" . $this->timePattern . "$/";
        // echo "Pattern: " . $this->rePattern;
    }

    public function calculateElapsedHours($timeRange, $round =true) {
        if ($timeRange == null) {
            return 0.0;
        }
        $tr = $this->convertToDateTime($timeRange);
        $interval = $tr['start']->diff($tr['end']);
        $strInterval = $interval->format('%H:%I:%S');
        $this->logger->debug('String Interval : ' . $strInterval);

        $elapsed = $this->stringToHours($strInterval);
        $this->logger->debug("Elapsed: $elapsed");
        if ($round) {
            $elapsed = round($elapsed, $this->precision);
        }
        return $elapsed;
    }

    public function isValidFormat($shift) {
        return preg_match($this->rePattern, $shift);
    }

    /*
     * Converts string time from 08:30 to 8.5.
     */

    public function stringToHours($time) {
        $parts = explode(":", $time);
        $hours = intval($parts[0]) + intval($parts[1]) / 60;
        $this->logger->debug("Time: " . $time);
        $this->logger->debug("Cantidad de Partes " . count($parts));
        $this->logger->debug("Partes[0]: " . $parts[0] . " Parts[1]: " . $parts[1]);
        $this->logger->debug("hours " . $hours);
        if (count($parts) == 3) {
            $this->logger->debug("Partes[2]: " . $parts[2]);
            $hours += intval($parts[2]) / 3600;
        }
        return $hours;
    }

    public function calculateWorkingHours($shifts) {
        $hours = 0;
        foreach ($shifts as $shift) {
            try {
                $el = $this->calculateElapsedHours($shift);
                $hours += $el;
                $this->logger->debug(" $shift :: $el HOURS:$hours ");
            } catch (Exception $ex) {
                $msg = "Error processing shift " . $shift . " | " . $ex->getMessage();
                throw new Exception($msg);
            }
        }
        return $hours;
    }

    public function convertToDateTime($timeRange, $relation = ShiftCalculator::SAME_DAY) {
        $basedate = "2010-01-01";
        if ($relation == ShiftCalculator::NEXT_DAY) {
            $basedate = "2010-01-02";
        } elseif ($relation == ShiftCalculator::PREVIOUS_DAY) {
            $basedate = "2009-12-31";
        }
        $times = explode("-", $timeRange);
        $start = new DateTime($basedate . " " . $times[0]);
        $end = new DateTime($basedate . " " . $times[1]);
        if ($end > $start) {
            return array("start" => $start, 'end' => $end);
        } else {
            $end->add(new DateInterval("P1D"));
            return array("start" => $start, 'end' => $end);
        }
    }

    public function areOverlaped($dtContainer, $dtContained) {
        if (($dtContainter['start'] <= $dtContained['start'] &&
                $dtContained['start'] <= $dtContainter['end']) ||
                ($dtContainter['end'] <= $dtContained['end'] &&
                $dtContained['end'] <= $dtContainter['end'])) {
            return TRUE;
        } else {
            if ($this->areOverlaped($dtContained, $dtContainer)) {
                return TRUE;
            } else {
                return FALSE;
            }
        }
    }

    /**
     * $second minus %first if difference is less than zero returns zero.
     * @param DateTime $first youngest occurrence
     * @param DateTime $second oldest ocurrrence
     * @return float   Hours difference
     */
    public function getPositiveDiff(DateTime $first, DateTime $second) {
        $interval = $first->diff($second);
        //echo "\tInterval: " . $interval->format('%R%H:%I:%S') ."\n";
        if ($interval->format('%R') == '+') {
            return $this->stringToHours($interval->format('%H:%I:%S'));
        } else {
            return 0.0;
        }
    }

    public function distributeHours($shiftContainer, $shiftContained, $containedNextDay =FALSE) {
        $dh = array('before' => 0, 'inside' => 0, 'after' => 0);
        $dtContainer = $this->convertToDateTime($shiftContainer);
        if($shiftContained == null){
            return $dh;
        }
        $dtContained = $this->convertToDateTime($shiftContained, $containedNextDay);
        //BEFORE
        // echo "*** BEFORE *****\n";
        $dh['before'] = $this->getPositiveDiff($dtContained['start'], $dtContainer['start']) -
                $this->getPositiveDiff($dtContained['end'], $dtContainer['start']);

        //echo "*** AFTER *****\n";
        $dh['after'] = $this->getPositiveDiff($dtContainer['end'], $dtContained['end']) -
                $this->getPositiveDiff($dtContainer['end'], $dtContained['start']);
        //INSIDE
        $dh['inside'] = $this->calculateElapsedHours($shiftContained) - $dh['before'] - $dh['after'];

        return $dh;
    }

    public function isContained($shiftContainer, $shiftContained) {
        if (!$this->isValidFormat($shiftContainer)) {
            $msg = "Container %s is an invalid shift format, format is 00:00-00:00";
            $msg = sprintf($msg, $shift);
            throw new Exception($msg);
        }
        if (!$this->isValidFormat($shiftContained)) {
            $msg = "Contained %s is an invalid shift format, format is 00:00-00:00";
            $msg = sprintf($msg, $shift);
            throw new Exception($msg);
        }
        $dtContainter = $this->convertToDateTime($shiftContainer);
        $dtContained = $this->convertToDateTime($shiftContained);
        if ($dtContainter['start'] <= $dtContained['start'] &&
                $dtContained['start'] <= $dtContainter['end'] &&
                $dtContainter['start'] <= $dtContained['end'] &&
                $dtContained['end'] <= $dtContainter['end']) {
            return TRUE;
        } else {
            return FALSE;
        }
    }

}

?>
